package org.lele.utils;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.ibm.icu.impl.data.ResourceReader;
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder;
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
import org.apache.pdfbox.util.Matrix;
import org.lele.pdf.DefaultPageSize;
import org.lele.pdf.PdfWatermarkProperties;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @Author: fanYuLe
 * @CreateTime: 2024-10-31  10:49
 * @Description:
 */
@Slf4j
public class PdfUtils {

    private static final PdfWatermarkProperties properties = new PdfWatermarkProperties();


    public static String htmlFileToBase64(String filePath) {
        File file = new File(filePath);
        try (InputStream is = new FileInputStream(file)) {
            byte[] buffer = new byte[(int) file.length()];
            is.read(buffer);
            return Base64.getEncoder().encodeToString(buffer);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }


    public static byte[] htmlFileToByteArray(String filePath) {
        File file = new File(filePath);
        byte[] fileBytes = new byte[(int) file.length()];

        try (FileInputStream fis = new FileInputStream(file)) {
            fis.read(fileBytes);
            return fileBytes;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static JSONObject setJsonObjectValue() {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("paymentName", "1" );
        jsonObject.put("num", "1");
        jsonObject.put("projectName", "1");
        jsonObject.put("TotalMoney", "1");
        jsonObject.put("engineerName","1");
        jsonObject.put("shouldDeducte", "1");
        jsonObject.put("collectionName", "1");
        jsonObject.put("payContent", "1");
        jsonObject.put("havePay", "1");
        jsonObject.put("thisTimePay","1");
        jsonObject.put("thisTimeShouldPay", "1");
        jsonObject.put("accumulatePay","1");
        jsonObject.put("fillUser", "1");
        jsonObject.put("accumulatePayRatio","1");
        jsonObject.put("mark","");
        jsonObject.put("fillDate", "1");
        jsonObject.put("shouldPay","1");
        jsonObject.put("comment1","");
        jsonObject.put("sign1","");
        jsonObject.put("date1","");
        jsonObject.put("comment12","");
        jsonObject.put("sign12","");
        jsonObject.put("date12","");

        jsonObject.put("comment2","");
        jsonObject.put("sign2","");
        jsonObject.put("date2","");
        jsonObject.put("comment22","");
        jsonObject.put("sign22","");
        jsonObject.put("date22","");

        jsonObject.put("comment3","");
        jsonObject.put("sign3","");
        jsonObject.put("date3","");
        jsonObject.put("comment32","");
        jsonObject.put("sign32","");
        jsonObject.put("date32","");
        jsonObject.put("comment33","");
        jsonObject.put("sign33","");
        jsonObject.put("date33","");

        jsonObject.put("comment4","");
        jsonObject.put("sign4","");
        jsonObject.put("date4","");

        jsonObject.put("comment5","");
        jsonObject.put("sign5","");
        jsonObject.put("date5","");
        return jsonObject;
    }

    public static MultipartFile genPdfByStringTemplateAndMakeWater(String templateContent, JSONObject data, String fileName, String waterMarkName) {
        if (!StringUtils.isBlank(templateContent) && data != null && !data.isEmpty()) {
            waterMarkName = String.format("%s%s", StringUtils.isNotBlank(waterMarkName) ? waterMarkName+"-" : "", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            MultipartFile pdfFile = genPdfByStringTemplate(templateContent, data, fileName, (DefaultPageSize)null);
            return getMultipartFile(fileName, waterMarkName, pdfFile);
        } else {
            return null;
        }
    }

    public static MultipartFile genPdfByStringTemplate(String templateContent, JSONObject data, String fileName, DefaultPageSize defaultPageSize) {
        Configuration configuration = new Configuration(Configuration.getVersion());
        configuration.setDefaultEncoding("utf-8");

        String htmlContent;
        try {
            StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
            configuration.setTemplateLoader(stringTemplateLoader);
            Template template = new Template("", templateContent, configuration);
            htmlContent = FreeMarkerTemplateUtils.processTemplateIntoString(template, data);
            if (properties.getDebug()) {
                log.info(">>>>>>>>>>>>>htmlContent:{}", htmlContent);
            }
        } catch (TemplateException | IOException var9) {
            log.error("生成模板失败", var9);
            throw new RuntimeException("生成模板失败",var9);
        }

        ByteArrayOutputStream outputStream = htmlToPdf(htmlContent, defaultPageSize);
        fileName = fileName + ".pdf";
        return new MockMultipartFile(fileName, fileName, "application/pdf", outputStream.toByteArray());
    }

    private static MultipartFile getMultipartFile(String fileName, String waterMarkName, MultipartFile pdfFile) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        try {
            addTextWatermark((InputStream)pdfFile.getInputStream(), (OutputStream)byteArrayOutputStream, waterMarkName);
        } catch (URISyntaxException | IOException var6) {
            log.error("添加水印失败", var6);
            throw new RuntimeException("添加水印失败",var6);
        }

        fileName = fileName + ".pdf";
        return new MockMultipartFile(fileName, fileName, "application/pdf", byteArrayOutputStream.toByteArray());
    }

    public static ByteArrayOutputStream htmlToPdf(String htmlContent, DefaultPageSize defaultPageSize) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        try {
            PdfRendererBuilder builder = new PdfRendererBuilder();
            builder.toStream(outputStream);
            builder.withHtmlContent(htmlContent, "");
            builder.useFont(() -> {
                try {
                    return ((URL) Objects.requireNonNull(ResourceReader.class.getClassLoader().getResource("fonts/" + properties.getFont() + ".ttf"))).openStream();
                } catch (IOException var2) {
                    log.error("htmlToPdf error", var2);
                   throw new RuntimeException("字体读取失败",var2);
                }
            }, "simfang");
            if (defaultPageSize != null) {
                builder.useDefaultPageSize(defaultPageSize.getPageWidth(), defaultPageSize.getPageHeight(), BaseRendererBuilder.PageSizeUnits.INCHES);
            }

            builder.run();
            return outputStream;
        } catch (IOException var5) {
            log.error("htmlToPdf error", var5);
            throw new RuntimeException("html转pdf失败",var5);
        }
    }

    public static void addTextWatermark(InputStream inputStream, OutputStream outputStream, String markWater) throws IOException, URISyntaxException {
        PDDocument document = PDDocument.load(inputStream);
        addTextToDocument(document, markWater);
        document.save(outputStream);
    }

    private static void addTextToDocument(PDDocument document, String markWater) throws IOException, URISyntaxException {
        document.setAllSecurityToBeRemoved(true);
        Iterator var3 = document.getPages().iterator();

        while(var3.hasNext()) {
            PDPage page = (PDPage)var3.next();
            PDPageContentStream stream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true);
            PDType0Font font;

                InputStream resourceAsStream = ResourceReader.class.getClassLoader().getResourceAsStream("fonts/" + properties.getFont() + ".ttf");
                font = PDType0Font.load(document, (InputStream)Objects.requireNonNull(resourceAsStream));


            PDExtendedGraphicsState r = new PDExtendedGraphicsState();
            r.setNonStrokingAlphaConstant(properties.getTransparency());
            r.setAlphaSourceFlag(true);
            stream.setGraphicsStateParameters(r);
            int[] color = properties.getColor();
            stream.setNonStrokingColor(color[0], color[1], color[2], color[3]);
            stream.beginText();
            stream.setFont(font, properties.getFontSize());
            float pageHeight = page.getMediaBox().getHeight();
            float pageWidth = page.getMediaBox().getWidth();

            for(int h = properties.getY(); (float)h < pageHeight; h += properties.getHeight()) {
                for(int w = properties.getX(); (float)w < pageWidth; w += properties.getWidth()) {
                    stream.setTextMatrix(Matrix.getRotateInstance(properties.getRotate(), (float)w, (float)h));
                    stream.showText(markWater);
                }
            }

            stream.endText();
            stream.restoreGraphicsState();
            stream.close();
        }

    }


    public static void download(MultipartFile file, HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        OutputStream outputStream = null;
        try {
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
            response.setContentType("application/octet-stream");
            outputStream = response.getOutputStream();
            try (InputStream fileIn = file.getInputStream()) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = fileIn.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            }
        } catch (IOException e) {
            log.error("文件下载失败", e);
            throw new RuntimeException("文件下载失败",e);
        } finally {
            try {
                assert outputStream != null;
                outputStream.close();
            } catch (IOException e) {
                log.error("outputStream.close error");
            }
        }
    }





    //第二套
    public MultipartFile genPdfByTemplateAndMakeWater(String templatePath, JSONObject data, String fileName, String waterMarkName) {
        return this.genPdfByTemplateAndMakeWater(templatePath, data, fileName, waterMarkName, (String)null);
    }

    public MultipartFile genPdfByTemplateAndMakeWater(String templatePath, JSONObject data, String fileName, String waterMarkName, String resourcePath) {
        waterMarkName = String.format("%s-%s", StringUtils.isNotBlank(waterMarkName) ? waterMarkName : "", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        MultipartFile pdfFile = null;
        if (resourcePath == null) {
            pdfFile = this.genPdfByTemplate(templatePath, data, fileName, (DefaultPageSize)null);
        } else {
            pdfFile = this.genPdfByTemplate(templatePath, data, fileName, (DefaultPageSize)null, resourcePath);
        }

        return this.getMultipartFile(fileName, waterMarkName, pdfFile);
    }

    public MultipartFile genPdfByTemplate(String templatePath, JSONObject data, String fileName, DefaultPageSize defaultPageSize) {
        return this.genPdfByTemplate(templatePath, data, fileName, defaultPageSize, (String)null);
    }
    public MultipartFile genPdfByTemplate(String templatePath, JSONObject data, String fileName, DefaultPageSize defaultPageSize, String resourcePath) {
        Configuration configuration = new Configuration(Configuration.getVersion());
        configuration.setDefaultEncoding("utf-8");

        String htmlContent;
        try {
            if (resourcePath == null) {
                configuration.setClassForTemplateLoading(PdfUtils.class, "/");
            } else {
                configuration.setDirectoryForTemplateLoading(new File(resourcePath));
            }

            Template template = configuration.getTemplate("template/" + templatePath);
            htmlContent = FreeMarkerTemplateUtils.processTemplateIntoString(template, data);
            if (this.properties.getDebug()) {
                log.info(">>>>>>>>>>>>>htmlContent:{}", htmlContent);
            }
        } catch (TemplateException | IOException var9) {
            log.error("生成模板失败", var9);
            throw new RuntimeException("生成模板失败",var9);
        }

        ByteArrayOutputStream outputStream = this.htmlToPdf(htmlContent, defaultPageSize);
        fileName = fileName + ".pdf";
        return new MockMultipartFile(fileName, fileName, "application/pdf", outputStream.toByteArray());
    }

    private static File multipartFileToFile(MultipartFile multipartFile) {
        File file = new File("/tmp/" + multipartFile.getName());
        InputStream inputStream = null;
        FileOutputStream outputStream = null;

        try {
            inputStream = multipartFile.getInputStream();
            outputStream = new FileOutputStream(file);
            byte[] bytes = new byte[1024];

            int len;
            while((len = inputStream.read(bytes)) > 0) {
                outputStream.write(bytes, 0, len);
            }
        } catch (Exception var13) {
            throw new RuntimeException(var13);
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }

                if (outputStream != null) {
                    inputStream.close();
                }

                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException var12) {
                log.error("multiparFileToFile error", var12);
            }

        }

        return file;
    }

    public void addTextWatermark(File sourceFile, String descFile, String markWater) throws IOException, URISyntaxException {
        PDDocument document = PDDocument.load(sourceFile);
        this.addTextToDocument(document, markWater);
        document.save(descFile);
        document.close();
    }

    private String uploadFile(String url) {
        try {
            if (url == null) {
                throw new RuntimeException("模板URL不能为空");
            } else {
                String fileName = UUID.randomUUID() + "_template.html";
                String basePath = "/static/gtreeFile/template/";
                File fileExist = new File(basePath);
                if (!fileExist.exists()) {
                    fileExist.mkdirs();
                }

                File file = new File(basePath, fileName);
                HttpUtil.downloadFile(url, file);
                return fileName;
            }
        } catch (Exception var6) {
            log.error("uploadFile error", var6);
            throw new RuntimeException("下载模板失败");
        }
    }


    public static byte[] loadHtmlFileAsByteArray(String filePath) throws IOException {
        // 使用ClassLoader获取资源输入流
        InputStream inputStream = ResourceReader.class.getClassLoader().getResource(filePath).openStream();
        if (inputStream == null) {
            throw new IOException("文件未找到: " + filePath);
        }
        // 使用ByteArrayOutputStream来收集字节
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        int nRead;
        byte[] data = new byte[16384]; // 16KB的缓冲区
        while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
            buffer.write(data, 0, nRead);
        }
        buffer.flush();
        return buffer.toByteArray();
    }

    public static void compressFilesToZip(List<MultipartFile> files, String zipName, HttpServletResponse response) {
        if (CollectionUtil.isEmpty(files)) {
            return;
        }
        try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
            String encodedZipName = URLEncoder.encode(zipName, StandardCharsets.UTF_8.toString());
            // 设置响应头，告诉浏览器下载文件
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=\"" + encodedZipName + ".zip\"");
            response.setContentType("application/octet-stream");
            for (MultipartFile file : files) {
                // 获取文件名
                String fileName = file.getOriginalFilename();
                // 创建一个新的ZipEntry对象，并设置名称
                ZipEntry zipEntry = new ZipEntry(StringUtils.isBlank(fileName) ? UUID.randomUUID().toString() : fileName);
                zipOut.putNextEntry(zipEntry);
                // 将文件内容写入ZipOutputStream
                try (InputStream fileIn = file.getInputStream()) {
                    byte[] buffer = new byte[1024];
                    int bytesRead;
                    while ((bytesRead = fileIn.read(buffer)) != -1) {
                        zipOut.write(buffer, 0, bytesRead);
                    }
                }
                zipOut.closeEntry();
            }
        } catch (IOException e) {
            log.error("生成zip文件失败", e);
            throw new RuntimeException("生成zip文件失败");
        }
    }
}
